package cn.turboinfo.fuyang.api.domain.admin.usecase.company;

import cn.turboinfo.fuyang.api.domain.common.service.custom.ServiceCustomService;
import cn.turboinfo.fuyang.api.domain.common.service.division.DivisionService;
import cn.turboinfo.fuyang.api.domain.common.service.order.ServiceOrderService;
import cn.turboinfo.fuyang.api.domain.common.service.staff.HousekeepingStaffService;
import cn.turboinfo.fuyang.api.domain.common.usecase.AbstractUseCase;
import cn.turboinfo.fuyang.api.domain.util.IdCardUtils;
import cn.turboinfo.fuyang.api.entity.common.enumeration.custom.ServiceCustomStatus;
import cn.turboinfo.fuyang.api.entity.common.enumeration.order.ServiceOrderStatus;
import cn.turboinfo.fuyang.api.entity.common.pojo.custom.ServiceCustom;
import cn.turboinfo.fuyang.api.entity.common.pojo.division.Division;
import cn.turboinfo.fuyang.api.entity.common.pojo.order.ServiceOrder;
import cn.turboinfo.fuyang.api.entity.common.pojo.staff.HousekeepingStaff;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Positive;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 家政公司可分配服务人员列表
 * 过滤掉已经被分配的服务人员
 *
 * @author sunshow
 */
@Slf4j
@RequiredArgsConstructor
@Component
public class CompanyAvailableAssignStaffListUseCase extends AbstractUseCase<CompanyAvailableAssignStaffListUseCase.InputData, CompanyAvailableAssignStaffListUseCase.OutputData> {

    private final ServiceOrderService serviceOrderService;

    private final ServiceCustomService serviceCustomService;

    private final HousekeepingStaffService housekeepingStaffService;

    private final DivisionService divisionService;

    @Override
    protected OutputData doAction(InputData inputData) {

        Long serviceOrderId = inputData.getServiceOrderId();
        Long companyId = inputData.getCompanyId();

        ServiceOrder serviceOrder = serviceOrderService.getByIdEnsure(serviceOrderId);

        if (!Objects.equals(companyId, serviceOrder.getCompanyId())) {
            throw new IllegalArgumentException("订单所属公司不匹配");
        }
        if (serviceOrder.getOrderStatus() != ServiceOrderStatus.PENDING_DISPATCH) {
            throw new IllegalArgumentException("服务订单状态不正确");
        }

        // 默认服务区间
        LocalDateTime start = serviceOrder.getScheduledStartTime();
        LocalDateTime end = serviceOrder.getScheduledEndTime() == null ? start.plusHours(2) : serviceOrder.getScheduledEndTime();

        // 本公司所有服务人员列表
        List<HousekeepingStaff> allStaffList = housekeepingStaffService.findByCompanyId(companyId);

        // 本公司所有已派单和正在服务的订单 用于过滤时间冲突的服务人员
        List<ServiceOrder> assignedOrderList = serviceOrderService.findByCompanyId(companyId,
                Set.of(ServiceOrderStatus.WAITING_STAFF_CONFIRM, ServiceOrderStatus.DISPATCHED_WAITING_SERVICE, ServiceOrderStatus.IN_SERVICE));

        // 本公司所有已派单和正在服务的订单 用于过滤时间冲突的服务人员
        List<ServiceCustom> assignedCustomStaffIdSet = serviceCustomService.findByCompanyId(companyId,
                Set.of(ServiceCustomStatus.WAITING_STAFF_CONFIRM, ServiceCustomStatus.DISPATCHED_WAITING_SERVICE, ServiceCustomStatus.IN_SERVICE));


        // 筛选时间冲突的人员
        Set<Long> assignedStaffIdSet = assignedOrderList.stream()
                .filter(order -> {
                    // 对应订单的开始时间或者结束时间在服务区间内即认为冲突
                    LocalDateTime ostart = order.getScheduledStartTime();
                    LocalDateTime oend = order.getScheduledEndTime() == null ? ostart.plusHours(2) : order.getScheduledEndTime();
                    if (ostart.isAfter(end)) {
                        return false;
                    }
                    if (oend.isBefore(start)) {
                        return false;
                    }
                    return true;
                })
                .map(ServiceOrder::getStaffId)
                .collect(Collectors.toSet());

        // 本公司所有已派单和正在服务的订单 用于过滤时间冲突的服务人员
        assignedStaffIdSet.addAll(
                assignedCustomStaffIdSet.stream()
                        .filter(order -> {
                            // 对应订单的开始时间或者结束时间在服务区间内即认为冲突
                            LocalDateTime ostart = order.getScheduledStartTime();
                            LocalDateTime oend = order.getScheduledEndTime() == null ? ostart.plusHours(2) : order.getScheduledEndTime();
                            if (ostart.isAfter(end)) {
                                return false;
                            }
                            if (oend.isBefore(start)) {
                                return false;
                            }
                            return true;
                        })
                        .map(ServiceCustom::getStaffId)
                        .collect(Collectors.toSet())
        );

        // 过滤掉冲突的人员之后返回列表
        List<HousekeepingStaff> staffList = allStaffList.stream()
                .filter(staff -> !assignedStaffIdSet.contains(staff.getId()))
                .toList();

        Set<Long> provinceIdSet = staffList.stream()
                .map(HousekeepingStaff::getProvinceCode)
                .collect(Collectors.toSet());

        Map<Long, String> divisionMap = divisionService.findByIdCollection(provinceIdSet)
                .stream()
                .collect(Collectors.toMap(Division::getId, Division::getAreaName));

        staffList.stream()
                .peek(staff -> {
                    // 设置年龄
                    staff.setAge(IdCardUtils.countAge(staff.getIdCard()));
                    if (divisionMap.containsKey(staff.getProvinceCode())) {
                        staff.setProvinceName(divisionMap.get(staff.getProvinceCode()));
                    }
                })
                .toList();

        return OutputData.builder()
                .staffList(staffList)
                .build();
    }

    @EqualsAndHashCode(callSuper = true)
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public static class InputData extends AbstractUseCase.InputData {

        /**
         * 所属公司ID
         */
        @NotNull(message = "所属公司不能为空")
        @Positive
        private Long companyId;

        @NotNull(message = "要分配的服务订单ID不能为空")
        @Positive
        private Long serviceOrderId;

    }

    @Getter
    @Setter
    @Builder
    public static class OutputData extends AbstractUseCase.OutputData {

        private List<HousekeepingStaff> staffList;

    }
}
